package pool

import (
	"context"
	"errors"
	"net"
	"sync"
	"time"
)

type connReq chan net.Conn

type Pool struct {
	// 空闲连接队列
	idleConns chan *idleConn
	// 请求队列
	reqQueue []connReq
	// 最大连接数
	maxCnt int
	// 当前连接数
	cnt int
	// 最大空闲时间
	maxIdleTime time.Duration
	factory     func() (net.Conn, error)
	lock        sync.Mutex
}

type idleConn struct {
	conn           net.Conn
	lastActiveTime time.Time
}

// Get
//  要考虑:
//    • 有空闲连接，直接返回
//    • 否则，没超过最大连接数，直接创建新的
//    • 否则，阻塞调用方
func (p *Pool) Get(ctx context.Context) (net.Conn, error) {
	// 先检测是否超时
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	default:
	}

	for {
		select {
		case ic := <-p.idleConns: // 空闲连接队列中有连接
			// 当前空闲连接已经空闲超过了指定的最大空闲时间
			// 需要关掉这个连接
			if ic.lastActiveTime.Add(p.maxIdleTime).Before(time.Now()) {
				_ = ic.conn.Close()
				continue
			}
			return ic.conn, nil
		default: // 没有空闲连接的时候
			p.lock.Lock()
			if p.cnt >= p.maxCnt { // 当前连接数超过最大连接数，要将当前 gorutine 阻塞(入队到请求队列中)，直到有人归还了连接
				req := make(connReq, 1)
				p.reqQueue = append(p.reqQueue, make(connReq, 1))
				p.lock.Unlock()

				select {
				case <-ctx.Done():
					// 选项1：从队列里面删掉 req 自己
					// 选项2：在这里转发
					go func() {
						c := <-req
						_ = p.Put(context.Background(), c)
					}()
					return nil, ctx.Err()
				// 有别人归还
				case c := <-req:
					return c, nil
				}
			}

			// 没有空闲连接，当前连接数也少于最大连接数，就创建一个
			c, err := p.factory()
			if err != nil {
				return nil, err
			}
			p.cnt++
			p.lock.Unlock()
			return c, nil
		}
	}
}

// Put
//   要考虑:
//    • 有 Get 请求被阻塞，把连接丢过去
//    • 否则，没超过最大空闲连接数，放到空闲列表
//    • 否则，直接关闭
func (p *Pool) Put(ctx context.Context, conn net.Conn) error {
	p.lock.Lock()
	if len(p.reqQueue) > 0 { // 请求队列里面有数据，说明有阻塞的请求
		// 从请求队列中拿出一个队列
		req := p.reqQueue[0]
		p.reqQueue = p.reqQueue[1:]
		p.lock.Unlock()
		req <- conn
		return nil
	}
	//defer p.lock.Unlock()
	// 没有阻塞的请求
	ic := &idleConn{
		conn:           conn,
		lastActiveTime: time.Now(),
	}
	select {
	case p.idleConns <- ic: // 没超过最大空闲连接数，放到空闲列表
	default: // 空闲队列满了
		_ = conn.Close()
		p.lock.Lock()
		p.cnt--
		p.lock.Unlock()
	}
	return nil
}

func NewPool(initCnt int, maxIdleCnt int, maxCnt int,
	maxIdleTime time.Duration,
	factory func() (net.Conn, error)) (*Pool, error) {
	if initCnt > maxIdleCnt {
		return nil, errors.New("micro: 初始连接数量不能大于最大空闲连接数量")
	}
	idlesConns := make(chan *idleConn, maxCnt)
	for i := 0; i < initCnt; i++ {
		conn, err := factory()
		if err != nil {
			return nil, err
		}
		idlesConns <- &idleConn{conn: conn, lastActiveTime: time.Now()}
	}
	return &Pool{
		idleConns:   idlesConns,
		reqQueue:    make([]connReq, 0),
		maxCnt:      maxCnt,
		cnt:         0,
		maxIdleTime: maxIdleTime,
		factory:     factory,
	}, nil
}
